package com.bcx.wind.workflow.imp.cmd.corecmd;

import com.bcx.wind.workflow.access.QueryFilter;
import com.bcx.wind.workflow.core.constant.*;
import com.bcx.wind.workflow.core.flow.NodeModel;
import com.bcx.wind.workflow.core.flow.node.ProcessModel;
import com.bcx.wind.workflow.entity.*;
import com.bcx.wind.workflow.errorcontext.WindError;
import com.bcx.wind.workflow.interceptor.Context;
import com.bcx.wind.workflow.pojo.Task;
import com.bcx.wind.workflow.pojo.Wind;
import com.bcx.wind.workflow.pojo.WindConfig;
import com.bcx.wind.workflow.pojo.WindUser;
import com.bcx.wind.workflow.pojo.variable.BuildVariable;
import com.bcx.wind.workflow.support.Assert;
import com.bcx.wind.workflow.support.JsonHelper;
import com.bcx.wind.workflow.support.ObjectHelper;
import com.bcx.wind.workflow.support.TimeHelper;

import java.util.List;
import java.util.Map;

import static com.bcx.wind.workflow.access.AccessSqlConstant.DESC;
import static com.bcx.wind.workflow.core.constant.Constant.JSON_NULL;
import static com.bcx.wind.workflow.core.constant.Constant.WIND_ADMIN;
import static com.bcx.wind.workflow.message.ErrorCode.*;

/**
 * 创建工作流命令
 *
 * @author zhanglei
 */
public class BuildCommand extends BaseCommand {

    /**
     * 创建参数
     */
    private BuildVariable variable;

    public BuildCommand(BuildVariable variable){
        this.variable = variable;
    }

    @Override
    public Wind execute(){
        this.wind().setOperate(WorkflowOperate.build);
        //校验创建参数
        checkBuildVariable();
        //对业务数据ID加锁
        getLock().lock(this.variable.getBusinessId());
        try{
            build();
        }finally {
            //解锁
            getLock().unlock(this.variable.getBusinessId());
        }
        return this.wind();
    }


    private void  build(){
        //设置默认值
        setDefaultWindValue();
        //创建
        buildWindOrder();
    }


    private void setDefaultWindValue(){
        this.wind().setBusinessId(this.variable.getBusinessId())
                .setUser(user())
                .setOperator(user().getUserId())
                .setSystem(variable.getSystem());
        if(variable.getArgs() != null){
            this.wind().setArgs(this.variable.getArgs());
        }

        if(variable.getVars() != null){
            this.wind().setVars(this.variable.getVars());
        }
    }


    private void  buildWindOrder(){
        QueryFilter filter = new QueryFilter()
                .setBusinessId(this.variable.getBusinessId())
                .setOrderType(OrderType.main.name());
        List<WindOrder> orders = commandContext.access().selectOrderInstanceList(filter);

        if(orders.isEmpty()){
            List<WindHistOrder> histOrders = commandContext.access().selectWindHistOrderList(filter);
            Assert.notEmptyError(ORDER_IS_COMPLETE,!histOrders.isEmpty());
        }else{
            WindOrder windOrder = orders.get(0);
            Assert.isTrueError(ORDER_IS_STOP_STATUS,windOrder.getStatus().equals(OrderStatus.stop.name()));

            WindOrder order = orders.get(0);
            this.wind().setWindOrder(order);
            String processId = order.getProcessId();
            //流程定义
            buildProcess(processId);
            //构建当前任务以及配置和流程配置
            buildCurNodeOrConfig();
            return;
        }

        //需要启动新的流程定义
        if(this.variable.getProcessId() != null){
            buildProcess(this.variable.getProcessId());
        }else if(this.variable.getProcessName() != null){
            buildProcessByName(this.variable.getProcessName());
        }
        buildCurNodeOrConfig();
    }


    private void  buildCurNodeOrConfig(){
        //流程配置
        getConfigList(wind().getWindProcess().getId());
        //设置流程配置
        setProcessConfig(this.variable.getArgs());
        //当前任务节点
        setCurTask();
    }


    /**
     * 设置当前任务节点
     */
    private  void  setCurTask(){

        WindOrder order = this.wind().getWindOrder();
        if(order != null){
            //获取当前任务
            QueryFilter filter = new QueryFilter()
                    .setOrderId(order.getId())
                    .setStatus(TaskStatus.run.name())
                    .setTaskLevel(TaskLevel.main.name());
            //如果当前流程操作不止一次，即当前流程不是初始化流程
            if(order.getVersion() > 1){
                 filter.setTaskActorId(new String[]{user().getUserId()});
            }
            //时间排序
            filter.setOrderBy(DESC);
            List<WindTask> tasks = commandContext.access().selectTaskInstanceList(filter);
            Assert.notEmptyError(NOT_FOUND_TASK,tasks);
            //设置当前任务，默认使用自己的最新任务
            WindTask windTask = tasks.get(0);
            Task task = buildCurNodeConfig(windTask.getTaskName(),windTask);
            this.wind().getCurNode().add(task);
            return;
        }
        //启动一个新的流程
        startProcess();
    }





    private  void  startProcess(){
        //获取流程首个任务节点
        ProcessModel processModel = this.wind().getWindProcess().processModel();
        NodeModel firstTask = processModel.getFirstTaskNode();
        //创建新的流程实例
        buildOrder();
        //设置当前任务以及当前任务配置
        Task task = buildCurNodeConfig(firstTask.nodeId(),null);
        //将数据设置到任务节点中，然后流转
        NodeModel startNode = processModel.getStartNode();
        startNode.context(this.commandContext,task);
        startNode.execute();
        //删除本地数据
        Context.remove();

        //设置task
        this.wind().getCurNode().add(task);
    }


    /**
     * 创建新的流程实例
     */
    private  void buildOrder(){
        WindOrder order = new WindOrder()
                .setId(ObjectHelper.primaryKey())
                .setProcessId(this.wind().getWindProcess().getId())
                .setBusinessId(this.variable.getBusinessId())
                .setStatus(OrderStatus.run.name())
                .setType(OrderType.main.name())
                .setCreateTime(TimeHelper.nowDate())
                .setCreateUser(this.wind().getUser() == null ? WIND_ADMIN : this.wind().getUser().getUserId())
                .setExpireTime(this.variable.getExpireTime())
                .setVersion(1)
                .setVariable(buildOrderVariable())
                .setData(this.variable.getOrderMsg())
                .setSystem(this.variable.getSystem());
        order = commandContext.runtimeService().createOrder(order);
        this.wind().setWindOrder(order);
    }


    /**
     * 构建流程实例变量
     * @return  变量字符串
     */
    private String buildOrderVariable(){
        Map<String,Object> vars = this.variable.getVars();
        if(!ObjectHelper.isEmpty(vars)){
            return JsonHelper.toJson(vars);
        }
        return JSON_NULL;
    }


    /**
     * 设置当年流程实例 全局配置
     *
     * @param args       匹配数据
     */
    private void  setProcessConfig(Map<String,Object> args){
        WindProcessConfig config = ObjectHelper.getNodeConfig(this.configList,this.wind().getWindProcess().getProcessName(),this.wind().getArgs());
        Assert.notEmptyError(PROCESS_CONFIG_NOT_FOUND,config,
                this.wind().getWindProcess().getProcessName(),this.wind().getWindProcess().getProcessName());
        assert config != null;
        WindConfig windConfig = buildWindConfig(config);
        this.wind().setWindConfig(windConfig);
    }




    /**
     * 构建流程定义
     * @param processId  流程定义ID
     */
    private void  buildProcess(String processId){
        WindProcess process = commandContext.getConfiguration().getProcess(processId);
        if(process == null){
            process = commandContext.repositoryService().queryById(processId);
        }
        Assert.notEmptyError(PROCESS_QUERY_NULL,process,processId);
        this.wind().setWindProcess(process);
    }

    /**
     * 构建流程定义
     * @param processName  流程定义名称
     */
    private void buildProcessByName(String processName){
        WindProcess process = commandContext.repositoryService().queryMaxVersion(processName);
        Assert.notEmptyError(PROCESS_QUERY_NULL,process,processName);
        this.wind().setWindProcess(process);
        this.variable.setProcessId(process.getId());
    }


    /**
     * 校验创建工作流参数是否合法
     */
    private void checkBuildVariable(){
        if(variable == null){
            WindError.error(BUILD_FLOW_FAIL,null,"variable");
        }
        Assert.notEmptyError(BUILD_FLOW_FAIL,this.variable.getBusinessId(),"businessId");
        if(ObjectHelper.allEmpty(this.variable.getProcessId(),this.variable.getProcessName())){
            WindError.error(BUILD_FLOW_FAIL,null,"processName or processId");
        }
    }


    /**
     * 当前用户  默认使用 wind_admin
     * @return  当前用户
     */
    private WindUser  user(){
        WindUser user = this.variable.getUser();
        if(user == null){
            return new WindUser()
                    .setUserId(WIND_ADMIN)
                    .setUserName("管理员");
        }
        //获取当前用户的委任用户，如果没有则直接用当前用户
        return user;
    }




}
